﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ExpreRec.Logic.Model;
using System.IO;
using ExpreRec.Logic.Util;
using ExpreRec.Logic.FaceDetection;
using OpenCvSharp;

namespace ExpreRec.Logic.Expression
{
    public class Classification
    {
        private static HaarFaceDetector faceDetector = new HaarFaceDetector();

        private static GaborExtract extract = new GaborExtract();

        public KeyValuePair<IList<string>, IList<PictureItem>> ListPictures(string folderName)
        {
            IList<PictureItem> res = new List<PictureItem>();
            IList<string> labels = new List<string>();

            if (!string.IsNullOrWhiteSpace(folderName) && Directory.Exists(folderName))
            {
                IEnumerable<string> directories = Directory.EnumerateDirectories(folderName);

                foreach (string labelDirectory in directories)
                {
                    string[] splits = labelDirectory.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);

                    string label = splits[splits.Length - 1].ToLower();

                    IEnumerable<string> files = Directory.EnumerateFiles(labelDirectory);

                    labels.Add(label);

                    foreach (string filename in files)
                    {
                        string ext = Path.GetExtension(filename).ToUpper();

                        if (ext.Equals(".TIFF")
                            || ext.Equals(".BMP")
                            || ext.Equals(".JPG")
                            || ext.Equals("PNG"))
                        {
                            PictureItem item = new PictureItem();
                            item.FileName = filename;
                            item.Label = label;

                            res.Add(item);
                        }
                    }
                }
            }
            return new KeyValuePair<IList<string>, IList<PictureItem>>(labels, res);
        }

        public void GaborKNNPreProcess(PictureItem item)
        {
            string label = item.Label;

            if (!Directory.Exists(Const.GaborTrainingKNNBaseFolder))
                Directory.CreateDirectory(Const.GaborTrainingKNNBaseFolder);


            string resultFolderLabel = Const.GaborTrainingKNNBaseFolder + "\\" + label.ToLower();

            if (!Directory.Exists(resultFolderLabel))
                Directory.CreateDirectory(resultFolderLabel);


            IplImage image = null;
            try
            {
                image = new IplImage(item.FileName);
            }
            catch (Exception)
            { }

            if (image != null)
            {
                IList<IplImage> faces = faceDetector.Detect(image);

                if (faces != null && faces.Count != 0)
                {
                    IplImage face = faces[0];

                    IplImage[] temp;

                    double[] result = extract.ExtractFeatures(face, out temp);

                    string fileNameWithoutExt = Path.GetFileNameWithoutExtension(item.FileName);

                    string dataName = Const.GaborTrainingKNNBaseFolder + "\\" + item.Label + "\\" + fileNameWithoutExt + ".data";

                    BinaryFileHelper.WriteData(result, dataName);
                }
            }
        }

        public KeyValuePair<IList<string>, IList<GaborItem>> GaborKNNLoadModel()
        {
            IList<GaborItem> res = new List<GaborItem>();
            IList<string> labels = new List<string>();

            if (Directory.Exists(Const.GaborTrainingKNNBaseFolder))
            {
                IEnumerable<string> directories = Directory.EnumerateDirectories(Const.GaborTrainingKNNBaseFolder);

                foreach (string labelDirectory in directories)
                {
                    string[] splits = labelDirectory.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);

                    string label = splits[splits.Length - 1].ToLower();

                    IEnumerable<string> files = Directory.EnumerateFiles(labelDirectory);

                    labels.Add(label);

                    foreach (string filename in files)
                    {
                        GaborItem item = new GaborItem();
                        item.Label = label;
                        item.Features = BinaryFileHelper.GetData(filename);
                        item.FileName = filename;

                        res.Add(item);
                    }
                }
            }
            return new KeyValuePair<IList<string>, IList<GaborItem>>(labels, res);
        }

        public int GaborKNNLeaveOneOutClassification(IList<string> labels, IList<GaborItem> models, bool distVote, double r, int k)
        {
            int count = 0;

            for (int i = 0; i < models.Count; i++)
            {
                IList<GaborItem> lib = new List<GaborItem>();

                for (int p = 0; p < models.Count; p++)
                {
                    if (i != p)
                        lib.Add(models[p]);
                }

                IList<KNNItem> classificationResult = GaborKNNClassification(labels, lib, models[i].Features, distVote, r, k);

                if (classificationResult[0].Label.Equals(models[i].Label))
                    count++;

                Console.WriteLine(classificationResult[0].Label + " VS " + models[i].Label);
                foreach (KNNItem item in classificationResult)
                {
                    Console.WriteLine("Confidence({0})={1}", item.Label, item.V);
                }
            }

            return count;
        }

        public IList<KNNItem> GaborKNNClassification(IList<string> labels, IList<GaborItem> lib, double[] unknown, bool distVote, double r, int k)
        {
            IDictionary<string, KNNItem> res = new Dictionary<string, KNNItem>();

            foreach (string label in labels)
            {
                KNNItem item = new KNNItem();
                item.Label = label;
                res.Add(label, item);
            }

            IList<KNNItem> KNNCompareResult = new List<KNNItem>();

            foreach (GaborItem item in lib)
            {
                double cnt = 0;

                for (int i = 0; i < unknown.Length; i++)
                {
                    cnt += Math.Pow(Math.Abs(item.Features[i] - unknown[i]), r);
                }

                cnt = Math.Pow(cnt, 1.0 / r);

                KNNItem KNNResultItem = new KNNItem();
                KNNResultItem.Label = item.Label;
                KNNResultItem.V = cnt;

                KNNCompareResult.Add(KNNResultItem);
            }

            IEnumerable<KNNItem> query = KNNCompareResult.OrderBy(item => item.V);

            int count = 0;

            foreach (KNNItem item in query)
            {
                count++;

                if (count > k)
                    break;

                if (distVote)
                    res[item.Label].V -= 1 / (item.V + 0.0001);
                else res[item.Label].V--;
            }

            IList<KNNItem> temp = res.Values.OrderBy(item => item.V).ToList<KNNItem>();
            IList<KNNItem> final = new List<KNNItem>();

            foreach (KNNItem resultItem in temp)
            {
                if (resultItem.V != 0)
                {
                    resultItem.V = Math.Abs(resultItem.V);

                    final.Add(resultItem);
                }
            }

            return final;
        }
    }
}
